﻿using HQHS.Library.Common.Colloection;
using HQHS.Library.Common.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace HQHS.Library.Common.Factory
{
    internal class ClassRegistry
    {
        // Fields
        private static readonly MissingValueDictionary<Type, Type> _classMap = new MissingValueDictionary<Type, Type>(new MissingValueDictionary<Type, Type>.MissingValueTryGetDelegate(ClassRegistry.TryGetUncachedType));
        private static readonly Dictionary<ConstructorKey, ConstructorInfo> _constructorDictionary = new Dictionary<ConstructorKey, ConstructorInfo>();
        private static readonly KeyedReaderWriterLockSlim<ConstructorKey> _constructorLock = new KeyedReaderWriterLockSlim<ConstructorKey>();

        // Methods
        private ClassRegistry()
        {
        }

        public static ConstructorInfo GetConstructor(Type type, object[] parameterValues)
        {
            Type[] parameterTypes = Type.GetTypeArray(parameterValues);
            return GetConstructor(type, parameterTypes);
        }

        private static ConstructorInfo GetConstructor(Type type, Type[] parameterTypes)
        {
            ConstructorInfo info2 = null;
            ConstructorKey key = new ConstructorKey(type, parameterTypes);
            if (!_constructorDictionary.TryGetValue(key, out info2))
            {
                _constructorLock.EnterWriteLock(key);
                try
                {
                    if (_constructorDictionary.TryGetValue(key, out info2))
                    {
                        return info2;
                    }
                    if (typeof(ISingleton).IsAssignableFrom(type))
                    {
                        if (parameterTypes.Length > 0)
                        {
                            throw new Exception("Classes implementing ISingleton may not have parameters, why are you passing some?");
                        }
                        ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                        if (constructors.Length > 1)
                        {
                            throw new Exception("Classes implementing ISingleton may not have more then one constructor");
                        }
                        if (constructors[0].IsPublic)
                        {
                            throw new Exception("Classes implementing ISingleton may not have a public constructor");
                        }
                        if (constructors[0].GetParameters().Length > 0)
                        {
                            throw new Exception("Classes implementing ISingleton may not take any parameters");
                        }
                        info2 = constructors[0];
                    }
                    else
                    {
                        info2 = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, parameterTypes, null);
                    }
                    _constructorDictionary[key] = info2;
                }
                finally
                {
                    _constructorLock.ExitWriteLock(key);
                }
            }
            return info2;
        }

        public static void RegisterCreate()
        {
        }

        private static bool TryGetUncachedType(Type inType, ref Type mappedType_out)
        {
            bool flag;
            DefaultImplementationClassAttribute[] customAttributes = (DefaultImplementationClassAttribute[])inType.GetCustomAttributes(typeof(DefaultImplementationClassAttribute), false);
            if (customAttributes.Length == 1)
            {
                if (customAttributes[0].ClassType != null)
                {
                    mappedType_out = customAttributes[0].ClassType;
                }
                else
                {
                    mappedType_out = Type.GetType(customAttributes[0].ClassName);
                }
                return true;
            }
            mappedType_out = null;
            return false;
        }

        public static bool TryRemapType(Type inType, ref Type outType)
        {
            return _classMap.TryGetValue(inType, ref outType);
        }

        // Nested Types
        private sealed class ConstructorKey
        {
            // Fields
            private int _hashCode;
            private Type[] _parameterTypes;
            private Type _type;

            // Methods
            public ConstructorKey(Type type, Type[] parameterTypes)
            {
                this._type = type;
                this._parameterTypes = parameterTypes;
                this._hashCode = this.GetHashCode(type, parameterTypes);
            }

            public override bool Equals(object obj)
            {
                bool flag2 = false;
                if (obj is ClassRegistry.ConstructorKey)
                {
                    ClassRegistry.ConstructorKey key = (ClassRegistry.ConstructorKey)obj;
                    if (this._type.Equals(key._type) && (this._parameterTypes.Length == key._parameterTypes.Length))
                    {
                        int num2 = this._parameterTypes.Length - 1;
                        for (int i = 0; i <= num2; i++)
                        {
                            if (!this._parameterTypes[i].Equals(key._parameterTypes[i]))
                            {
                                return false;
                            }
                        }
                        flag2 = true;
                    }
                }
                return flag2;
            }

            public override int GetHashCode()
            {
                return this._hashCode;
            }

            private int GetHashCode(Type type, Type[] parameterTypes)
            {
                string str = null;
                StringBuilder builder = new StringBuilder();
                foreach (Type type2 in parameterTypes)
                {
                    builder.Append(str);
                    if (type2 == null)
                    {
                        builder.Append("null");
                    }
                    else
                    {
                        builder.Append(type2.FullName);
                    }
                    str = ":";
                }
                return (type.GetHashCode() ^ builder.ToString().GetHashCode());
            }
        }
    }
}
